﻿/**
 * 工具函数包
 * @file
 * @module puxu.utils
 * @since 1.1.0.0
 */

var utils = (puxu.utils = {
  /**
   * 用给定的迭代器遍历数组或类数组对象
   * @method each
   * @param { Array } array 需要遍历的数组或者类数组
   * @param { Function } iterator 迭代器， 该方法接受两个参数， 第一个参数是当前所处理的value， 第二个参数是当前遍历对象的key
   * @example
   * ```javascript
   * var divs = document.getElmentByTagNames("div");
   * //output: 0: DIV, 1: DIV ...
   * puxu.utils.each(divs, funciton (value, key) {
   *    console.log(key + ":" + value.tagName);
   * });
   * ```
   */
  each: function (obj, iterator, context) {
    if (obj == null) return;
    if (obj.length === +obj.length) {
      for (var i = 0, l = obj.length; i < l; i++) { if (iterator.call(context, obj[i], i, obj) === false) return false; }
    } else {
      for (var key in obj) { if (obj.hasOwnProperty(key)) { if (iterator.call(context, obj[key], key, obj) === false) return false; } }
    }
  },
  /**
   * 将source对象中的属性扩展到target对象上， 根据指定的isKeepTarget值决定是否保留目标对象中与
   * 源对象属性名相同的属性值。
   * @method extend
   * @param { Object } target 目标对象， 新的属性将附加到该对象上
   * @param { Object } source 源对象， 该对象的属性会被附加到target对象上
   * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
   * @return { Object } 返回target对象
   * @example
   * ```javascript
   * var target = { name: 'target', sex: 1 }, source = { name: 'source', age: 17 };
   * puxu.utils.extend(target, source, true);
   * //output:{ name: 'target', sex: 1, age: 17 }
   * console.log(target);
   * ```
   */
  extend: function (t, s, b) {
    if (s) { for (var k in s) { if (!b || !t.hasOwnProperty(k)) { t[k] = s[k]; } } }
    return t;
  },
  /**
   * 动态加载文件到doc中，加载成功后执行的回调函数fn
   * @method loadFile
   * @param { DomDocument } document 需要加载资源文件的文档对象
   * @param { Object } options 加载资源文件的属性集合， 该集合支持的值是script标签和style标签支持的所有属性。
   * @param { Function } fn 资源文件加载成功之后执行的回调
   * @warning 对于在同一个文档中多次加载同一URL的文件， 该方法会在第一次加载之后缓存该请求，在此之后的所有同一URL的请求， 将会直接触发回调。
   * @example
   * ```javascript
   * puxu.utils.loadFile(document, {
   *     src:"test.js",
   *     tag:"script",
   *     type:"text/javascript",
   *     defer:"defer"
   * }, function () {
   *     console.log('加载成功');
   * });
   * ```
   */
  loadFile: (function () {
    var tmpList = [];

    function getItem(doc, obj) {
      try {
        for (var i = 0, ci; (ci = tmpList[i++]);) { if (ci.doc === doc && ci.url == (obj.src || obj.href)) { return ci; } }
      } catch (e) {
        return null;
      }
    }
    return function (doc, obj, fn) {
      var item = getItem(doc, obj);
      if (item) {
        if (item.ready) { fn && fn(); } else { item.funs.push(fn); }
        return;
      }
      tmpList.push({ doc: doc, url: obj.src || obj.href, funs: [fn] });
      if (!doc.body) {
        var html = [];
        for (var p in obj) { if (p == "tag") continue; html.push(p + '="' + obj[p] + '"'); }
        doc.write("<" + obj.tag + " " + html.join(" ") + " ></" + obj.tag + ">");
        return;
      }
      if (obj.id && doc.getElementById(obj.id)) { return; }
      var element = doc.createElement(obj.tag);
      delete obj.tag;
      for (var p in obj) { element.setAttribute(p, obj[p]); }
      element.onload = element.onreadystatechange = function () {
        if (!this.readyState || /loaded|complete/.test(this.readyState)) {
          item = getItem(doc, obj);
          if (item.funs.length > 0) { item.ready = 1; for (var fi; (fi = item.funs.pop());) { fi(); } }
          element.onload = element.onreadystatechange = null;
        }
      };
      element.onerror = function () { throw Error("The load " + (obj.href || obj.src) + " fails,check the url settings of file puxu.config.js "); };
      doc.getElementsByTagName("head")[0].appendChild(element);
    }
  })(),
  /**
   * 深度克隆对象，将source的属性克隆到target对象， 会覆盖target重名的属性。
   * @method clone
   * @param { Object } source 源对象
   * @param { Object } target 目标对象
   * @return { Object } 附加了source对象所有属性的target对象
   */
  clone: function (source, target) {
    var tmp;
    target = target || {};
    for (var i in source) {
      if (source.hasOwnProperty(i)) {
        tmp = source[i];
        if (typeof tmp == "object") {
          target[i] = utils.isArray(tmp) ? [] : {}; utils.clone(source[i], target[i]);
        } else {
          target[i] = tmp;
        }
      }
    }
    return target;
  },
  /**
   * 将str中的html符号转义,将转义“'，&，<，"，>，”，“”七个字符
   * @method unhtml
   * @param { String } str 需要转义的字符串
   * @return { String } 转义后的字符串
   * @example
   * ```javascript
   * var html = '<body>&</body>';
   * //output: &lt;body&gt;&amp;&lt;/body&gt;
   * console.log(puxu.utils.unhtml(html));
   * ```
   */
  unhtml: function (str, reg) {
    return str ? str.replace(
      reg || /[&<">'](?:(amp|lt|ldquo|rdquo|quot|gt|#39|nbsp|#\d+);)?/g,
      function (a, b) { if (b) { return a; } else { return { "<": "&lt;", "&": "&amp;", '"': "&quot;", "“": "&ldquo;", "”": "&rdquo;", ">": "&gt;", "'": "&#39;" }[a]; } }
    ) : "";
  }
});
/**
 * 判断给定的对象是否是字符串
 * @method isString
 * @param { * } object 需要判断的对象
 * @return { Boolean } 给定的对象是否是字符串
 */

/**
 * 判断给定的对象是否是数组
 * @method isArray
 * @param { * } object 需要判断的对象
 * @return { Boolean } 给定的对象是否是数组
 */

/**
 * 判断给定的对象是否是一个Function
 * @method isFunction
 * @param { * } object 需要判断的对象
 * @return { Boolean } 给定的对象是否是Function
 */

/**
 * 判断给定的对象是否是Number
 * @method isNumber
 * @param { * } object 需要判断的对象
 * @return { Boolean } 给定的对象是否是Number
 */

/**
 * 判断给定的对象是否是一个正则表达式
 * @method isRegExp
 * @param { * } object 需要判断的对象
 * @return { Boolean } 给定的对象是否是正则表达式
 */

/**
 * 判断给定的对象是否是一个普通对象
 * @method isObject
 * @param { * } object 需要判断的对象
 * @return { Boolean } 给定的对象是否是普通对象
 */
utils.each(
  ["String", "Function", "Array", "Number", "RegExp", "Object", "Date"],
  function (v) {
    puxu.utils["is" + v] = function (obj) { return Object.prototype.toString.apply(obj) == "[object " + v + "]"; };
  }
);